偶爾會看到鏈式寫法,但實際上自己寫package的時候,還不曾這樣玩過,這篇講解所謂的鏈式寫法,以及拿decimal package稍微做分析範例。
在golang第一次接觸的相關部分,就是standard library的time package了,實務上開發很容易就需要使用時間的處理。
『鏈式』的寫法就如以下這段code示範
package main
import (
"fmt"
"time"
)
func main() {
t1 := time.Now()
time.Sleep(1)
t2 := time.Now().Sub(t1).Seconds()
fmt.Println(t2)
}
time的三個method可以連續使用對吧,Now()、Sub()、Seconds(),就像個鏈子鏈在一起。
time.Now().Sub(t1).Seconds()
而且基於golang的設計原則,method name大小寫用途有所區分,所以看到這樣的使用方式,看到的method name基本上都會是大寫,除非你自己設計在自己的package,然後在這個package做內部使用而已,那筆者認為這意義其實不大。
不過筆者第一次接觸chain (鏈式寫法)並非是在golang或php,而是在jquery,jquery容易在使用上一個method接著一個method連續使用,那時就很好奇地問前輩,這到底是什麼原理,為什麼我自己寫的method沒辦法給別人這樣很方便的使用?
前輩告訴我一句話:
『那就是每個method做回傳的時候,回傳它自己呀。』
核心概念的確是這樣,筆者沒什麼好補充的,因為每個method拿到的回傳結果,如果是一個物件,物件又有屬於它的method,當然可以接著繼續使用method囉。
如下圖舉例,我的程式接連使用了NewFromFloat()、Add()、Float64()
allPoint,_ := decimal.NewFromFloat(currentPoint).Add(decimal.NewFromFloat(incomePoint)).Float64()
於是我們來看看,這三個method詳細是怎麼寫的
NewFromFloat()
func NewFromFloat(value float64) Decimal {
if value == 0 {
return New(0, 0)
}
return newFromFloat(value, math.Float64bits(value), &float64info)
}
Add()
func (d Decimal) Add(d2 Decimal) Decimal {
baseScale := min(d.exp, d2.exp)
rd := d.rescale(baseScale)
rd2 := d2.rescale(baseScale)
d3Value := new(big.Int).Add(rd.value, rd2.value)
return Decimal{
value: d3Value,
exp: baseScale,
}
}
Float64()
func (d Decimal) Float64() (f float64, exact bool) {
return d.Rat().Float64()
}
NewFromFloat()和Add()回傳的都是Decimal型態,所以可以繼續使用任何一個屬於Decimal的method,直到Float64(),就不再回傳Decimal型態,作為一個收尾的method。
那是因為外面能用這個Decimal的型態的方式非常有限,就是該package大寫提供出去的field和method而已,所以提供method,將最後的型態處理成golang的通用型態回傳,非常的合理合情。
最後我們來看一下,decimal型態長什麼樣子
type Decimal struct {
value *big.Int
exp int32
}
是個內部擁有兩個小寫變數的struct,所以看來只能透過package提供的method,對內容做操作。
值得一提的是,因為內部的數值是可能因method的使用而做改變,所以在使用method的順序上,可能要稍微注意,像是deciaml這種做數值四則運算處理的package,也許安排很簡單,但是像是gorm那種組query語法,開始者要自己清楚知道,鏈式的使用上『順序』很重要,要知道自己究竟最後組出了什麼樣的語法。
以上,有興趣也可以自己寫看看鏈式設計的package囉。